﻿using Games.Common;
using Games.Data;
using System;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace Games.Light
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    partial class Light : ContentPage
    {
        IDataBase database = DependencyService.Get<IDataBase>();
        private Game game;
        private readonly GameInfo gameInfo;
        private GameOptions gameOptions = new GameOptions();
        private LightGameArg lightGameArgs;
        const int CellSpacing = 2;
        int cellSize;
        int layoutWidth, layoutHeight;
        internal Light(GameInfo gamePage)
        {
            InitializeComponent();
            BindingContext = gameInfo = gamePage;
            game = new Game();
            LoadAsync().Wait();
            database.CreateTable<LightRecord>();
        }
        async Task LoadAsync()
        {
            var gameArgs = await database.GetFirstOrDefaultAsync<GameArgs>(x => x.GameID == gameInfo.ID).ConfigureAwait(false);
            if(gameArgs != null)
            {
                lightGameArgs = BaseArgs.Convert<LightGameArg>(gameArgs);
                gameInfo.Width = lightGameArgs.Width;
                gameInfo.Height = lightGameArgs.Height;
            }
            else
            {
                gameInfo.Width = gameInfo.Height = 3;
            }
        }
        async Task SaveArgsAsync()
        {
            if(lightGameArgs == null)
            {
                lightGameArgs = new LightGameArg()
                {
                    GameID = gameInfo.ID,
                    //Version = gameInfo.Version,
                };
            }
            lightGameArgs.Width = gameInfo.Width;
            lightGameArgs.Height = gameInfo.Height;
            await database.SaveAsync(lightGameArgs.ToGameArgs());
        }
        void OnLayoutSizeChanged(object sender, EventArgs args)
        {
            Layout layout = sender as Layout;
            layoutWidth = (int)layout.Width;
            layoutHeight = (int)layout.Height;
            game.Init(gameInfo.Width, gameInfo.Height);
            InitLayout();
        }

        void InitLayout()
        {
            gameInfo.ClickCount = 0;

            var sizew = (layoutWidth - CellSpacing * 2) / gameInfo.Width;
            var sizeh = (layoutHeight - CellSpacing * 2) / gameInfo.Height;
            cellSize = Math.Min(sizew, sizeh);
            var layWidth = gameInfo.Width * cellSize + CellSpacing;
            var layHeight = gameInfo.Height * cellSize + CellSpacing;
            absoluteLayout.WidthRequest = layWidth;
            absoluteLayout.HeightRequest = layHeight;
            var xMargin = (layoutWidth - layWidth) / 2.0;
            var yMargin = (layoutHeight - layHeight) / 2.0;
            absoluteLayout.Margin = new Thickness(xMargin, yMargin);
            var count = gameInfo.Width * gameInfo.Height;

            while(absoluteLayout.Children.Count > count)
            {
                absoluteLayout.Children.RemoveAt(0);
            }

            while(absoluteLayout.Children.Count < count)
            {
                GameCell lifeCell = new GameCell();
                lifeCell.Tapped += OnTapGestureTapped;
                absoluteLayout.Children.Add(lifeCell);
            }

            for(byte x = 0; x < gameInfo.Width; x++)
            {
                for(byte y = 0; y < gameInfo.Height; y++)
                {
                    var index = x * gameInfo.Height + y;
                    GameCell lifeCell = (GameCell)absoluteLayout.Children[index];
                    lifeCell.Col = x;
                    lifeCell.Row = y;
                    lifeCell.IsBright = false;
                    Rectangle rect = new Rectangle(x * cellSize + CellSpacing,
                                                   y * cellSize + CellSpacing,
                                                   cellSize - CellSpacing,
                                                   cellSize - CellSpacing);
                    AbsoluteLayout.SetLayoutBounds(lifeCell, rect);
                    lifeCell.Show();
                }
            }
            TimeCounter.Start();
        }
        async void OnTapGestureTapped(object sender, EventArgs args)
        {
            if(game.IsFinished)
            {
                return;
            }
            GameCell lifeCell = (GameCell)sender;
            game.Click(lifeCell.Col, lifeCell.Row);
            await lifeCell.ScaleBack(0.95);
            lifeCell.IsBright ^= true;

            TryChange(lifeCell.Col + 1, lifeCell.Row);
            TryChange(lifeCell.Col - 1, lifeCell.Row);
            TryChange(lifeCell.Col, lifeCell.Row + 1);
            TryChange(lifeCell.Col, lifeCell.Row - 1);
            gameInfo.ClickCount++;
            if(game.IsFinished)
            {
                await ShowFinish();
            }
        }
        async Task ShowFinish()
        {
            var record = await SaveRecordAsync();
            await DisplayAlert(record.Level, record.Message, "OK");
        }
        async Task<Record> SaveRecordAsync()
        {
            var dur = TimeCounter.Stop();
            var click = game.GetClick();
            var cRecord = new LightRecord()
            {
                Width = gameInfo.Width,
                Height = gameInfo.Height,
                GameID = gameInfo.ID,
                ClickCount = click,
                AllClickCount = gameInfo.ClickCount,
                CreateTime = DateTime.Now,
                Diff = gameInfo.Width * gameInfo.Height * 100 + 99 - Math.Abs(gameInfo.Width - gameInfo.Height),
                Duration = dur
            };
            await database.SaveRecordByLevel(cRecord,
                o => o.Width == cRecord.Width && o.Height == cRecord.Height,
                o => o.AllClickCount > cRecord.AllClickCount || (o.AllClickCount == cRecord.AllClickCount && o.Duration > cRecord.Duration)
            );
            return cRecord.Convert();
        }

        void TryChange(int x, int y)
        {
            if(game.InBoard(x, y))
            {
                var index = x * gameInfo.Height + y;
                GameCell lifeCell = (GameCell)absoluteLayout.Children[index];
                lifeCell.IsBright ^= true;
            }
        }

        private View CreateOptionsView()
        {
            var grid = new Grid { Padding = new Thickness(10), BackgroundColor = Color.White, RowSpacing = 0, ColumnSpacing = 0 };

            grid.RowDefinitions.Add(new RowDefinition { Height = 40 });
            grid.RowDefinitions.Add(new RowDefinition { Height = 40 });
            grid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Star });
            grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(2, GridUnitType.Star) });

            var layopt = new LayoutOptions(LayoutAlignment.Center, true);

            var width = new Stepper { Maximum = 12, Minimum = 3, Increment = 1, HorizontalOptions = layopt, BindingContext = gameOptions };
            var height = new Stepper { Maximum = 12, Minimum = 3, Increment = 1, HorizontalOptions = layopt, BindingContext = gameOptions };
            var lblWidth = new Label { Margin = new Thickness(0), HorizontalOptions = layopt, VerticalOptions = layopt, FontSize = 18, BindingContext = gameOptions };
            var lblHeight = new Label { Margin = new Thickness(0), HorizontalOptions = layopt, VerticalOptions = layopt, FontSize = 18, BindingContext = gameOptions };

            EventHandler<ValueChangedEventArgs> vh = (s, e) =>
            {
                width.IsEnabled = height.Value > 3;
                if(height.Value > 3)
                {
                    width.Maximum = height.Value;
                    if(width.Value > width.Maximum)
                    {
                        width.Value = width.Maximum;
                    }
                }
                else
                {
                    width.Maximum = 4;
                    width.Value = 3;
                }
            };
            height.ValueChanged += vh;
            vh.Invoke(null, null);

            width.SetBinding(Stepper.ValueProperty, "Width", BindingMode.TwoWay);
            height.SetBinding(Stepper.ValueProperty, "Height", BindingMode.TwoWay);
            lblWidth.SetBinding(Label.TextProperty, "Width", stringFormat: "宽度：{0}");
            lblHeight.SetBinding(Label.TextProperty, "Height", stringFormat: "高度：{0}");

            grid.Children.Add(lblWidth, 0, 0);
            grid.Children.Add(lblHeight, 0, 1);
            grid.Children.Add(width, 1, 0);
            grid.Children.Add(height, 1, 1);
            return grid;
        }

        void OptionsClicked(object sender, EventArgs args)
        {
            if(!mask.Contains("options"))
            {
                var optionView = CreateOptionsView();
                mask.Add("options", "选项设置", optionView);
                mask.SetSize("options", 0.8, 0.4);
                mask.Callback("options", async () =>
                {
                    gameInfo.Width = gameOptions.Width;
                    gameInfo.Height = gameOptions.Height;
                    if(!game.IsSize(gameInfo.Width, gameInfo.Height))
                    {
                        game.Init(gameInfo.Width, gameInfo.Height);
                        InitLayout();
                        await SaveArgsAsync();
                    }
                });
            }
            gameOptions.Height = gameInfo.Height;
            gameOptions.Width = gameInfo.Width;
            mask.ShowWithOkCancel("options");
        }
        void RestartClicked(object sender, EventArgs args)
        {
            game.Init(gameInfo.Width, gameInfo.Height);
            InitLayout();
        }

        void OnAboutButtonClicked(object sender, EventArgs args)
        {
            if(!mask.Contains("about"))
            {
                var aboutView = ControlUtil.GetAbout(gameInfo);
                mask.Add("about", gameInfo.Name, aboutView);
            }
            mask.ShowWithCancel("about", "确定");
        }
        void OnRecordClicked(object sender, EventArgs args)
        {
            //var sorter = new Sorter<LightRecord>(q => q.OrderByDescending(x => x.Diff).ThenBy(x => x.AllClickCount).ThenBy(x => x.ClickCount).ThenBy(x => x.Duration).ThenByDescending(x => x.ID));
            Navigation.PushAsync(new RecordPage(gameInfo.Name, new RecordViewModel<LightRecord>(null)));
        }

        private void TapToBack(object sender, EventArgs e)
        {
            Navigation.PopAsync();
        }
    }
}